package cn.tellsea.frame.shiro.matcher;

import cn.tellsea.frame.consts.RedisConst;
import cn.tellsea.frame.util.RedisUtils;
import cn.tellsea.module.system.entity.UserInfo;
import cn.tellsea.module.system.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 登陆次数限制
 *
 * @author Tellsea
 * @date 2021/04/12
 */
@Slf4j
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    /**
     * 错误次数缓存时间
     */
    private int redisKeyTime = 5 * 60;

    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //获取用户名
        String username = (String) token.getPrincipal();
        String redisKey = RedisConst.SHIRO_CACHE_RETRYLIMIT + username;
        //获取用户登录次数
        AtomicInteger retryCount = (AtomicInteger) redisUtils.get(redisKey);
        if (retryCount == null) {
            //如果用户没有登陆过,登陆次数加1 并放入缓存
            retryCount = new AtomicInteger(0);
            redisUtils.set(redisKey, retryCount, redisKeyTime);
        }
        if (retryCount.incrementAndGet() > 5) {
            //如果用户登陆失败次数大于5次 抛出锁定用户异常  并修改数据库字段
            UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getName, username));
            if (userInfo != null) {
                userInfo.setIsLock(2);
                userInfoService.updateById(userInfo);
            }
            log.info("锁定用户：{}" + username);
            //抛出用户锁定异常
            throw new LockedAccountException();
        }
        //判断用户账号和密码是否正确
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            //如果正确,从缓存中将用户登录计数 清除
            redisUtils.del(redisKey);
        } else {
            redisUtils.set(redisKey, retryCount, redisKeyTime);
        }
        return matches;
    }

    /**
     * 解锁用户
     *
     * @param username
     */
    public void unlockAccount(String username) {
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getName, username));
        userInfo.setIsLock(2);
        userInfoService.updateById(userInfo);
        redisUtils.del(RedisConst.SHIRO_CACHE_RETRYLIMIT + username);
    }
}
